iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0
Modern Web

初學者跪著學JavaScript系列 第 19

初學者跪著學JavaScript Day19 : 原型畢露(上)

  • 分享至 

  • xImage
  •  

一日客語:中文:拜託你 客語:bai togˋnˇ 掰偷 三聲n

物件都有原型,當自己屬性找不到時就會往原型找,然後一直找一直找除非找到,要不就找到null

物件:物件的原型繼承某個“一般物件”: 使用方式 Object.setPrototypeOf

物件: 物件的來源是“建構函式”: 使用方式 new constructor


物件的原型繼承某個“一般物件”

平常使用的物件方式

//像是雨傘學院,每人都有不同能力
const wendy = { fly: 30 };
const ann = { run: 50 };
const nick = { swimming: 100 };
console.log('fly' in wendy); //true
console.log('run' in wendy); //false

有個神奇想法
我想要ann的跑步能力!!!

把ann設為wendy的原型prototype
prototype可以在瀏覽器下看wendy物件[[prototype]],這裡觀察一下

要如何把ann變成我的原型?
使用Object.setPrototypeOf(繼承者物件,原型物件)

Hint:使用in可以找屬性

方式:
1.wendy原型設ann:

Object.setPrototypeOf(wendy, ann);
console.log('run' in wendy); //true`

我想要得到誰力量就把那人設成我的原型

2.那ann原型設nick呢?ann就會得到nick的力量

3.然後wendy也會繼承ann和nick的屬性,這樣wendy會飛、跑、游泳

Object.setPrototypeOf(wendy, ann);
console.log('run' in wendy); //true
//把ann的原型設為nick,想要游泳能力
Object.setPrototypeOf(ann, nick);
//身為ann繼承者wendy也具有游泳的能力
console.log('swimming' in wendy);//true

物件的來源是“建構函式”

  • function 也有原型物件(function(){}的prototype)
  • function創建出來的物件,物件的[[prototype]]會設為function(){}的prototype
    假設:Cat建構子,nini是實例
    也就是說Cat.prototype == nini.__proto__

最容易搞混的地方:
在瀏覽器觀察
建構函式有prototype屬性 =>Cat.prototype
nini(實例)有[[prototype]]屬性(但無法選取,只能靠其他方式) =>nini.__proto__

看到沒~就是這樣建立了不可告人的關係
還要注意建構子也有可能是別物件的實例
所以也會有prototype(會當某物件的爸)和[[prototype]](也可能是別人的兒子)


想法上:

在function Cat的原型(物件)加一個eat方法

Cat()

function Cat() {  }
Cat.prototype.eat = function () {
    return 'eat';
}
let b = Cat();//一般function
console.log(b); //undefined

但這樣只是呼叫function

根據剛剛說的 function 建出來的物件的原型也會是function prtototype

所以要思考如何才能讓function 建出物件? 使用new 運算子

new + function 會建立新物件:new Cat()

此function會是 constructor (建構器)按照慣例要大寫
所以cat()=>Cat()

function Cat() {}

Cat.prototype.eat = function () {
    return 'eat';
};

const a = new Cat();
console.log(a)//{}
console.log(a.eat());//eat

a是經由function 建立出的物件,這個物件的原型有eat方法

問題來了要如何在新物件上加上自帶屬性?? 可以使用this

new 運算子和this的關係

mdn:

The new operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function

new運算子根據內建或自己定義的constructor function 創建instance(實例)

  • 函式呼叫會將this傳進function內

    資料參考:忍者開發者技巧探秘第二版
function Cat(name) {
    this.name = name;
    
    
    this.sleep = function () {
        return 'sleeping';//這寫法不太好,晚點解釋
    };
}

let cat1 = new Cat('kiki');
let cat2 = new Cat('niki');
let cat3 = new Cat('nini');

console.log('cat1', cat1);
//cat1 Cat { name: 'kiki', sleep: [Function (anonymous)] }
console.log('cat2', cat2);
//cat2 Cat { name: 'niki', sleep: [Function (anonymous)] }
console.log('cat3', cat3);
//cat3 Cat { name: 'nini', sleep: [Function (anonymous)] }

第三步:書上會說新的空物件會被設定為函式背景空間

this指的是新建立的物件

Cat()function 沒有回傳(return)物件會回傳新建立的物件

所以記憶體會撥出一塊空間放新物件

函式原型有constructor屬性

函式原型有constructor屬性會指向(reference)到原來的函式


重點筆記:

cat1 增加屬性可以三種方式

1.自帶自身的屬性:使用this

2.增加原型的屬性

3.增加實例的屬性

function Cat() {
    this.name = 'kitty';
    //自帶自身的屬性
    this.sleep = function () {
        //自帶自身的屬性
        return 'sleeping';
    };
}

let cat1 = new Cat();
Cat.prototype.fruit = 'apple'; //增加原型的屬性
cat1.lay = 'lay'; //增加實例屬性

console.log(cat1.fruit); //apple
console.log(cat1.sleep()); //sleeping
console.log(cat1.lay); //lay


看prototype(原型)方式:

  • Object.__proto__
  • Object.getPrototypeOf()
function Cat(name) {
    this.name = name;
    this.sleep = function () {
        return 'sleeping';
    };
}

let cat1 = new Cat('kiki');
Cat.prototype.eat = 'apple';
Cat.prototype.eyes = 2;

console.log(Object.getPrototypeOf(cat1)); 
//{ eat: 'apple', eyes: 2 }
console.log(cat1.__proto__); 
//{ eat: 'apple', eyes: 2 }
console.log(cat1); 
//Cat { name: 'kiki', sleep: [Function (anonymous)] }

Cat原型(prototype)的constructor屬性指向Function

function Cat(name) {
    this.name = name;
    this.sleep = function () {
        return 'sleeping';
    };
}

let cat1 = new Cat('kiki');
Cat.prototype.eat = 'apple';
Cat.prototype.eyes = 2;
console.log(Object.getPrototypeOf(cat1).constructor);
//[Function: Cat]
console.log(cat1.__proto__.constructor);
//[Function: Cat]

當prototype和instance的方法同名一樣會先找到哪一個?

function內this.eat vs Cat.prototype.eat

會先選擇instance(實例)優先於prototype(原型)

function Cat(name) {
    this.name = name;
    this.sleep = function () {
        return 'sleeping';
    };
    this.eat = 'apple';//自身
}

Cat.prototype.eat = 'banana';//原型
Cat.prototype.eyes = 2;
let cat1 = new Cat('kiki');

console.log(cat1.eat); //apple

回到這樣寫法不好的原因

function Cat(name) {
    this.name = name;
    
    this.sleep = function () {
        return 'sleeping';//這寫法不太好,晚點解釋
    };
}

let cat1 = new Cat('kiki');
let cat2 = new Cat('niki');
let cat3 = new Cat('nini');

console.log('cat1', cat1);
//cat1 Cat { name: 'kiki', sleep: [Function (anonymous)] }
console.log('cat2', cat2);
//cat2 Cat { name: 'niki', sleep: [Function (anonymous)] }
console.log('cat3', cat3);
//cat3 Cat { name: 'nini', sleep: [Function (anonymous)] }

cat1、cat2、cat3都有各自sleep function,若當物件創立n個會有n個sleep function
在程式會希望相同邏輯會可以共用,儘可能省記憶體空間,因此加在Cat的原型下創建物件使用它的sleep function(只要一個)


原型我也還在學習,這是目前我理解的樣子
真是累的不要不要的QQ

(主要閱讀忍者開發技巧探秘第二版書籍)
資料來源:
忍者開發技巧探秘第二版
mdn


上一篇
初學者跪著學JavaScript Day18 : 物件:new Map()
下一篇
初學者跪著學JavaScript Day20 : 原型畢露(中)
系列文
初學者跪著學JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言